PersistentVolume
與 PersitentVolumeClaim
(以下簡稱 PV & PVC)的觀念通常很容易的與如何使服務 Stateful
的設定掛上關係,最重要的關鍵是 PV
的生命週期獨立於 Pod
之外,使得我們在消滅或擴展 Pod
時可以繼續持有原有的資料,而這些資料儲存的地方也從原本的 Pod
中,轉而由 NFS(Network File System)、叢集中的 Node
、雲端儲存服務等相關儲存空間來進行存放,進而實現獨立的生命週期。
Kubernetes
利用 PV
提供一個抽象的存儲空間,並且 PV
能被動態和靜態的被提供,可以簡單理解為當 PV
是預先被宣告出來後被 PVC
取用的話就是一種靜態,而如果 PVC
中有指定 storageclass
的種類時, Kubernetes
將會動態的為我們產生 PV
。
當我們作為一位系統管理者宣告了一塊存儲空間後,而系統的使用者就能以 PVC
來請求此存儲空間,形成下圖的關係:
在 PVC
中表達的是使用者對存儲的請求,相較於 Pod
可以請求特定數量的 CPU 和內存資源, PVC
也可以請求存儲空間大小以及設定訪問模式 (例如:ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany),而 PVC
的資源請求成立後,將會去不斷的找尋符合條件的 PV
直到找到符合條件的資源並且將兩者綁定,如果找不到匹配的 PV
時, PVC
將會無限期的處於未榜定狀態(Pending),直到出現與之匹配的 PV
加入。
# pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-demo
spec:
accessModes:
- ReadWriteOnce
storageClassName: hostpath
resources:
requests:
storage: 1Gi
以上我們使用 PVC
動態產生 PV
並且綁定兩者。
訪問模式(spec.accessModes):
ReadWriteOnce
卷可以被一個節點以讀寫方式掛載。ReadWriteOnce 訪問模式也允許運行在同一節點上的多個Pod 訪問卷。ReadOnlyMany
卷可以被多個節點以只讀方式掛載。ReadWriteMany
卷可以被多個節點以讀寫方式掛載。ReadWriteOncePod
卷可以被單個Pod 以讀寫方式掛載。如果你想確保整個集群中只有一個Pod 可以讀取或寫入該PVC, 請使用ReadWriteOncePod 訪問模式。訪問模式是 PV & PVC
中蠻值得注意的點,因為本系列文主要都是使用『單節點』的 Kubernetes (docker-desktop),並不會遇到不同節點中的 Pod
掛載 PV
的情況,而現實的生產環境裡,往往的我們使用的 Google GKE
或 AWS ELK
通常都是多節點的情況,這時不同節點之間 Pod
以及服務就必須指定 ReadOnlyMany
或 ReadWriteMany
才能順利取得共享資源,加上支持 ReadOnlyMany
ReadWriteMany
的 Provisioner 選擇不多,其中多半是使用雲端的 NFS 服務實現。
即使是相同服務的 Pod,也不一定會
Kubernetes
的資源分配器分配到同一個節點上,除非使用nodeSelector
等顯式隱性設定。
存儲類型(spec.storageClassName):
在 docker-desktop
中預設的 storageClass
為 hostpath
,可以很直觀的看出這個 storageClass
很大的概率是將其的存儲空間設置在節點上,所以我們在 docker-desktop
的這個單節點裡可以實現出 Pods
之間的共享數據 。 如果管理員所創建的所有靜態 PV
都無法與用戶的 PVC
匹配, 集群可以嘗試為該 PVC
的設置動態製備一個存儲卷。這一製備操作是基於 StorageClass
來實現的,如 PVC
沒有特別指定則會使用預設 StorageClass
。 PVC
必須請求某個 StorageClass
, 同時集群管理員必須已經創建並配置了該類,這樣動態製備卷的動作才會發生。如果PVC 申領指定存儲類為""
,則相當於為自身禁止使用動態製備的捲。
接下來產生我們的 PVC
:
kubectl apply -f ./pvc.yaml
----------
persistentvolumeclaim/pvc-demo created
查看結果:
kubectl get pv
----------
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-c197c285-d314-43db-8cbc-6f912d8a9680 1Gi RWO Delete Bound default/pvc-demo hostpath 2s
kubectl get pvc
----------
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-demo Bound pvc-c197c285-d314-43db-8cbc-6f912d8a9680 1Gi RWO hostpath 11s
可以看到 PV & PVC
都已經成功的綁定(Bound) 並且配置相關的資源。
這裡 PV
會有四種狀態 (STATUS)
Available
:表示 PV 為可用狀態Bound
:表示已綁定到 PVCReleased
:PVC 已被刪除,但是尚未回收Failed
:回收失敗PV
有三種回收策略 (RECLAIM POLICY),分別是
Retain
:手動回收Recycle
(已棄用):透過刪除命令 rm -rf /thevolume/*
,取而代之的建議方案是動態產生。Delete
:刪除 PV 的同時也會一併刪除後端儲存磁碟。這時我們再度拿出前幾天使用的 emptyDir volume
範例,將他由同個 Pod
分離成不同 Pod
來查看 PV & PVC
的生命週期是否獨立於 Pod
。
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: pvc-demo
readOnly: false
# alpine-pod.ymal
apiVersion: v1
kind: Pod
metadata:
name: alpine-pod
spec:
containers:
- name: alpine
image: alpine
command: [ "/bin/sh", "-c" ]
args: # 每十秒定時向 /html/index.html 寫入資料
- while true; do
echo $(hostname) $(date) >> /html/index.html;
sleep 10;
done
volumeMounts:
- name: html
mountPath: /html
volumes:
- name: html
persistentVolumeClaim:
claimName: pvc-demo
readOnly: false
將以上兩個掛在了 PVC
的 Pod
運行起來:
kubectl apply -f ./nginx-pod.yaml ./alpine-pod.yaml
-----------
pod/alpine-pod configured
pod/nginx-pod configured
來確認一下 alpine
容器每隔 10 秒像 html/index.html 寫入訊息,而 Nginx
容器掛載的 PVC
是否同時也可以取得更新。
將 port 導出到本地的 localhost :
kubectl port-forward pod/nginx-pod 8080:80
-------
Forwarding from 127.0.0.1:8080 -> 80pod 8080:80
Forwarding from [::1]:8080 -> 80
使用 curl 查看返回值:
curl http://localhost:8080
--------
alpine-pod Sat Aug 6 03:31:50 UTC 2022
alpine-pod Sat Aug 6 03:32:00 UTC 2022
alpine-pod Sat Aug 6 03:32:10 UTC 2022
alpine-pod Sat Aug 6 03:32:20 UTC 2022
alpine-pod Sat Aug 6 03:32:30 UTC 2022
順利取得由 alpine
容器產生的內容~
PV & PVC
的觀念很廣牽涉的範圍也很宏觀,理解起來需要對 Kubernetes
以及服務的運作場景都要有不少的理解。這幾天認真的鑽研了 PV & PVC
的觀念後,剛好公司就需要用到。只能說保持資料的持久性這方面真的是一塊大議題,在 StackOverflow 中有網友分享道: Kubernetes
的精神更適合拿用來做 Stateless
的微服務,並且將需要持久性的資料抽象出來做為一個獨立服務並開放 API 以供其他服務取得。
在最近的工作上也有更深的一層的認同,在小弟為了讓 Google GKE
上的多節點叢集實現共享持久性數據,就讓我把 Google 提供的 Kubernetes Provision storage 服務都實作過一次,分別是建立在 Google FileStore
的雲端 NFS 服務(實現了 ReadWriteMany),以及建立在 Google Compute Engine Disk
的雲端硬碟服務(實現了 ReadWriteOnce),兩種服務也對應了不同的情況,日後有機會可以再做一篇分享。
千呼萬喚始出來!鐵人賽系列「從異世界歸來發現只剩自己不會 Kubernetes」同名改編作品出版了!
感謝所有交流指教的各路英雄,也感謝願意點閱文章的各位,如果能幫助到任何人都將會是我的榮幸。
本書內容改編自第 14 屆 iThome 鐵人賽 DevOps 組的優選系列文章《從異世界歸來發現只剩自己不會 Kubernetes》。此書是一本綜合性的指南,針對想要探索認識 Kubernetes 的技術人員而生。無論是初涉此領域的新手,還是已有深厚經驗的資深工程師,本書都能提供你所需的知識和技能。
「這本書不僅提供了豐富的範例程式碼和操作指南,讓身為工程師的我們能實際操作來加深認知;更重要的是,它教會我如何從後端工程師的角度去思考和應用 Kubernetes。從容器的生命週期、資源管理到部署管理,每一章都與我們的日常開發工作息息相關。」
──── 雷N │ 後端工程師 / iThome 鐵人賽戰友
天瓏連結: 從異世界歸來發現只剩自己不會 Kubernetes:初心者進入雲端世界的實戰攻略!
相關文章:
相關程式碼同時收錄在:
https://github.com/MikeHsu0618/2022-ithelp/tree/master/Day20
Reference
Using the Compute Engine persistent disk CSI Driver
pvc.yaml 的spec.accessModes: 是ReadWriteOnce,意思是相同的node上面,每個pod都能夠掛載pvc-demo?
pvc.yaml 的spec.storageClassName: hostpath,為什麼它不用設定儲存位置? 是不是nginx-pod.yaml和alpine-pod.ymal 都在spec.container.image.mountPath設定儲存位置?
1.沒錯,但使用多個 pod 掛 RWO 的用法,在實務上侷限很大也比較不推薦。
2.是的 PVC 資源只是用來聲明該 PVC 使用的的是哪種 storage class,需要掛載到哪個路徑則是由 Workload Resource 上設定。